//
//  ZYANRMonitor.m
//  ZYWebImage
//
//  Created by wangzhipan on 2025/5/3.
//

#import "ZYANRMonitor.h"
#import "ZYCrashReportGenerator.h"
/*
 UI 刷新是在 Source0 中处理的。具体流程如下：

 1. UI 事件首先由 Source1 接收系统事件
 2. Source1 将事件转换为 Source0 事件
 3. Source0 触发回调，进行具体的 UI 刷新操作
 ## Source0 的主要职责
 1. 触摸事件处理
    
    - 手指触摸屏幕的事件
    - 手势识别
    - 点击事件响应
 2. UI 更新
    
    - 视图的更新
    - 约束的更新
    - 布局计算
 3. 定时器事件
    
    - NSTimer 的回调
    - 显示相关的定时操作
 ## Source1 的主要职责
 1. 系统事件处理
    
    - 接收系统事件
    - 处理各种硬件事件
 2. 进程间通信
    
    - 基于 mach port 的通信
    - 系统级别的通知
 3. 网络事件
    
    - 基于端口的网络通信
    - 系统网络状态变化
 */
@interface ZYANRMonitor() {
    dispatch_semaphore_t _semaphore;
    CFRunLoopObserverRef _runloopObserver;
    NSUInteger _count;
    CFRunLoopActivity _activity;
}
@property(atomic, assign) BOOL isMonitoring;
@end

@implementation ZYANRMonitor
+ (instancetype)sharedInstance
{
    static id _instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

- (instancetype)init
{
    if (self = [super init]) {
        
    }
    return self;
}

- (void)startMonitoring
{
    if (self.isMonitoring) {
        return;
    }
    
    self.isMonitoring = YES;
    [self p_registerRunloopObserver];
}

- (void)stop
{
    if (!self.isMonitoring) {
        return;
    }
    self.isMonitoring = NO;
    
    CFRunLoopRemoveObserver(CFRunLoopGetMain(), _runloopObserver, kCFRunLoopCommonModes);
    CFRelease(_runloopObserver);
    _runloopObserver = NULL;
    _semaphore = NULL;
}

#pragma mark - private
- (void)p_registerRunloopObserver
{
    _semaphore = dispatch_semaphore_create(0);
    
    __weak typeof(self) weakSelf = self;
    _runloopObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        if (!weakSelf) {
            return;
        }
        [weakSelf p_runloopObserverActivity:activity];
    });
    CFRunLoopAddObserver(CFRunLoopGetMain(), _runloopObserver, kCFRunLoopCommonModes);
    
    [self p_monitorThreadRun];
}

- (void)p_runloopObserverActivity:(CFRunLoopActivity)activity
{
    _activity = activity;
    dispatch_semaphore_signal(_semaphore);
}

- (void)p_monitorThreadRun
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        while (self.isMonitoring) {
            intptr_t result = dispatch_semaphore_wait(self->_semaphore, dispatch_time(DISPATCH_TIME_NOW, 100.0 / 1000 * NSEC_PER_SEC));
            
            //说明是超时
            if (result != 0) {
                if (self->_activity == kCFRunLoopBeforeSources) {
                    /*此时主线程正准备处理 Source0 事件
                      如果卡顿，可能是：
                             1. UI 刷新任务过重
                             2. 用户事件处理耗时
                             3. 其他同步任务阻塞
                     */
                    self->_count++;
                }
                else if (self->_activity == kCFRunLoopAfterWaiting) {
                    /* 此时主线程刚被唤醒
                       如果卡顿，可能是：
                              1. 大量事件待处理
                              2. 某个事件处理时间过长
                              3. 线程间锁竞争
                     */
                    self->_count++;
                }
                else {
                    /* 其他状态不计入卡顿统计
                          因为这些状态要么是正常的等待（BeforeWaiting）
                          要么是 RunLoop 状态切换（Entry/Exit）
                     */
                    self->_count = 0;
                }
                if (self->_count < 5) {
                    continue;
                }
                [self p_handleANRReport];
            }
            self->_count = 0;
        }
    });
}

- (void)p_handleANRReport
{
    NSString *name = @"Main Thread ANR!";
    NSTimeInterval duration = self->_count * 0.1;
    NSString *reason = [NSString stringWithFormat:@"主线程已经 %.2f 秒未响应，可能发生了ANR", duration];
    
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[@"duration"] = @(duration);
    
    // 生成崩溃报告
    [[ZYCrashReportGenerator sharedInstance] generateReportWithType:ZYCrashTypeANR
                                                               name:name
                                                             reason:reason
                                                           userInfo:userInfo];
}
@end
